package net.chrone.query.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Iterator;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.ConnectionPoolTimeoutException;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONObject;

import net.chrone.query.channels.WechatUtil;



public class HttpClientHelper{
	private static final Logger logger = Logger.getLogger(HttpClientHelper.class);
	public static final String GET = "GET";
	public static final String POST = "POST";
	//HTTP请求器
	private static CloseableHttpClient httpClient;
	//请求器的配置
	private static RequestConfig requestConfig;

	public static String getNvPairs(List<String[]> list, String charSet){
		if(list==null || list.size()==0){
			return null;
		}
		StringBuffer stringBuffer = new StringBuffer();
		for(int i=0; i<list.size(); i++){
			String[] nvPairStr = list.get(i);
			try{
				if(i>0){
					stringBuffer.append("&");
				}
				stringBuffer.append(URLEncoder.encode(nvPairStr[0], charSet)).append("=").append(URLEncoder.encode(nvPairStr[1], charSet));
			}catch(UnsupportedEncodingException e){
				e.printStackTrace();
				return null;
			}
		}
		return stringBuffer.toString();
	}

	
	
	public static String getNvPairsByJson(JSONObject json, String charSet){
		if(json==null || json.size()==0){
			return null;
		}
		StringBuffer stringBuffer = new StringBuffer();
		Iterator<String> iterator = json.keySet().iterator();
		while(iterator.hasNext()){
			String key = iterator.next();
			stringBuffer.append("&").append(key).append("=").append(json.get(key).toString());
			
		}
		
		return stringBuffer.toString().replaceFirst("[&]", "");
	}
	
	
	public static HttpResponse doHttp(String urlStr, String method, String charSet, String postStr, String timeOut){
		if(method==null || (!GET.equalsIgnoreCase(method) && !POST.equalsIgnoreCase(method))){
			return null;
		}
		URL url = null;
		try{
			url = new URL(urlStr);
		}catch(MalformedURLException e){
			e.printStackTrace();
			return null;
		}
		if("https".equalsIgnoreCase(urlStr.substring(0, 5))){
			SSLContext sslContext = null;
			try
			{
				sslContext = SSLContext.getInstance("TLS");
				X509TrustManager xtmArray[] = {
						new HttpX509TrustManager()
				};
				sslContext.init(null, xtmArray, new SecureRandom());
			}
			catch(GeneralSecurityException gse)
			{
				gse.printStackTrace();
			}
			if(sslContext != null)
				HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
			HttpsURLConnection.setDefaultHostnameVerifier(new HttpHostnameVerifier());
		}
		HttpURLConnection httpURLConnection = null;
		try{
			httpURLConnection = (HttpURLConnection)url.openConnection ();
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		httpURLConnection.setConnectTimeout(Integer.parseInt(timeOut));
		httpURLConnection.setReadTimeout(Integer.parseInt(timeOut));
		try{
			httpURLConnection.setRequestMethod(method.toUpperCase());
		}catch(ProtocolException e){
			e.printStackTrace();
			return null;
		}
		if(POST.equalsIgnoreCase(method)){
			httpURLConnection.setDoOutput(true);  
			PrintWriter printWriter = null;
			try{
				printWriter = new PrintWriter(new OutputStreamWriter(httpURLConnection.getOutputStream(), charSet));
			}catch(UnsupportedEncodingException e){
				e.printStackTrace();
				return null;
			}catch(IOException e){
				e.printStackTrace();
				return null;
			}  
			printWriter.write(postStr);
			printWriter.flush();
		}
		InputStream inputStream = null;  
		try{
			inputStream = httpURLConnection.getInputStream();
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		int data = 0;
		int statusCode = 0;
		try{
			statusCode = httpURLConnection.getResponseCode();
			if(statusCode<HttpURLConnection.HTTP_OK || statusCode>=HttpURLConnection.HTTP_MULT_CHOICE){
				HttpResponse httpRsponse = new HttpResponse();
				httpRsponse.setStatusCode(statusCode);
				return httpRsponse;
			}
			while((data=inputStream.read())!=-1){
				byteArrayOutputStream.write(data);
			}
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
		byte[] returnBytes = byteArrayOutputStream.toByteArray();
		String returnStr = null;
		try{
			returnStr = new String(returnBytes, charSet);
		}catch(UnsupportedEncodingException e){
			e.printStackTrace();
			return null;
		}
		HttpResponse httpRsponse = new HttpResponse();
		httpRsponse.setStatusCode(statusCode);
		httpRsponse.setRspStr(returnStr);
		return httpRsponse;
	}

	public static String doHttp(String urlStr, String method, List<String[]> headers, String reqCharSet, String postStr, String timeOut, String rspCharSet){
		if(method==null || (!GET.equalsIgnoreCase(method) && !POST.equalsIgnoreCase(method))){
			return null;
		}
		URL url = null;
		try{
			url = new URL(urlStr);
		}catch(MalformedURLException e){
			e.printStackTrace();
			return null;
		}
		if("https".equalsIgnoreCase(urlStr.substring(0, 5))){
			SSLContext sslContext = null;
			try
			{
				sslContext = SSLContext.getInstance("TLS");
				X509TrustManager xtmArray[] = {
						new HttpX509TrustManager()
				};
				sslContext.init(null, xtmArray, new SecureRandom());
			}
			catch(GeneralSecurityException gse)
			{
				gse.printStackTrace();
			}
			if(sslContext != null)
				HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
			HttpsURLConnection.setDefaultHostnameVerifier(new HttpHostnameVerifier());
		}
		HttpURLConnection httpURLConnection = null;
		try{
			httpURLConnection = (HttpURLConnection)url.openConnection ();
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		System.setProperty("sun.net.client.defaultConnectTimeout", timeOut);
		System.setProperty("sun.net.client.defaultReadTimeout", timeOut);
		try{
			if(headers!=null && headers.size()>0){
				for(int i=0; i<headers.size(); i++){
					String[] nvPairStr = headers.get(i);
					if(nvPairStr.length==2){
						httpURLConnection.setRequestProperty(nvPairStr[0], nvPairStr[1]);
					}
				}
			}
			httpURLConnection.setRequestMethod(method.toUpperCase());
		}catch(ProtocolException e){
			e.printStackTrace();
			return null;
		}
		if(POST.equalsIgnoreCase(method)){

			httpURLConnection.setDoOutput(true);  
			PrintWriter printWriter = null;
			try{
				printWriter = new PrintWriter(new OutputStreamWriter(httpURLConnection.getOutputStream(), reqCharSet));
			}catch(UnsupportedEncodingException e){
				e.printStackTrace();
				return null;
			}catch(IOException e){
				e.printStackTrace();
				return null;
			}  
			printWriter.write(postStr);
			printWriter.flush();
		}
		InputStream inputStream = null;  
		try{
			inputStream = httpURLConnection.getInputStream();
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		int data = 0;
		try{
			int statusCode = httpURLConnection.getResponseCode();
			if(statusCode<HttpURLConnection.HTTP_OK || statusCode>=HttpURLConnection.HTTP_MULT_CHOICE){
				return null;
			}
			while((data=inputStream.read())!=-1){
				byteArrayOutputStream.write(data);
			}
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
		byte[] returnBytes = byteArrayOutputStream.toByteArray();
		String returnStr = null;
		try{
			returnStr = new String(returnBytes, rspCharSet);
		}catch(UnsupportedEncodingException e){
			e.printStackTrace();
			return null;
		}
		return returnStr;
	}

	public static HttpResponse doHttp(String urlStr, String method, List<String[]> headers, String charSet, String postStr, String timeOut){
		if(method==null || (!GET.equalsIgnoreCase(method) && !POST.equalsIgnoreCase(method))){
			return null;
		}
		URL url = null;
		try{
			url = new URL(urlStr);
		}catch(MalformedURLException e){
			e.printStackTrace();
			return null;
		}
		if("https".equalsIgnoreCase(urlStr.substring(0, 5))){
			SSLContext sslContext = null;
			try
			{
				sslContext = SSLContext.getInstance("TLS");
				X509TrustManager xtmArray[] = {
						new HttpX509TrustManager()
				};
				sslContext.init(null, xtmArray, new SecureRandom());
			}
			catch(GeneralSecurityException gse)
			{
				gse.printStackTrace();
			}
			if(sslContext != null)
				HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
			HttpsURLConnection.setDefaultHostnameVerifier(new HttpHostnameVerifier());
		}
		HttpURLConnection httpURLConnection = null;
		try{
			httpURLConnection = (HttpURLConnection)url.openConnection ();
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		System.setProperty("sun.net.client.defaultConnectTimeout", timeOut);
		System.setProperty("sun.net.client.defaultReadTimeout", timeOut);
		try{
			if(headers!=null && headers.size()>0){
				for(int i=0; i<headers.size(); i++){
					String[] nvPairStr = headers.get(i);
					if(nvPairStr.length==2){
						httpURLConnection.setRequestProperty(nvPairStr[0], nvPairStr[1]);
					}
				}
			}
			httpURLConnection.setRequestMethod(method.toUpperCase());
		}catch(ProtocolException e){
			e.printStackTrace();
			return null;
		}
		if(POST.equalsIgnoreCase(method)){
			httpURLConnection.setDoOutput(true);  
			PrintWriter printWriter = null;
			try{
				printWriter = new PrintWriter(new OutputStreamWriter(httpURLConnection.getOutputStream(), charSet));
			}catch(UnsupportedEncodingException e){
				e.printStackTrace();
				return null;
			}catch(IOException e){
				e.printStackTrace();
				return null;
			}  
			printWriter.write(postStr);
			printWriter.flush();
		}
		InputStream inputStream = null;  
		try{
			inputStream = httpURLConnection.getInputStream();
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		int data = 0;
		int statusCode = HttpURLConnection.HTTP_OK;
		try{
			statusCode = httpURLConnection.getResponseCode();
			if(statusCode<HttpURLConnection.HTTP_OK || statusCode>=HttpURLConnection.HTTP_MULT_CHOICE){
				HttpResponse httpRsp = new HttpResponse();
				httpRsp.setStatusCode(statusCode);
				return httpRsp;
			}
			while((data=inputStream.read())!=-1){
				byteArrayOutputStream.write(data);
			}
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		byte[] returnBytes = byteArrayOutputStream.toByteArray();
		String returnStr = null;
		try{
			returnStr = new String(returnBytes, charSet);
		}catch(UnsupportedEncodingException e){
			e.printStackTrace();
			return null;
		}
		HttpResponse httpRsp = new HttpResponse();
		httpRsp.setStatusCode(statusCode);
		httpRsp.setRspStr(returnStr);
		return httpRsp;
	}

	/**
	 * 加载证书
	 * @param path
	 * @throws IOException
	 * @throws KeyStoreException
	 * @throws UnrecoverableKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public static  void initCert(String path,String mch_id) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
		KeyStore keyStore = KeyStore.getInstance("PKCS12");

		//加载本地的证书进行https加密传输
		FileInputStream instream = new FileInputStream(new File(path));
		try {
			//加载证书密码，默认为商户ID
			keyStore.load(instream, mch_id.toCharArray());  
		} catch (CertificateException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} finally {
			instream.close();
		}
		//加载证书密码，默认为商户ID
		SSLContext sslcontext = SSLContexts.custom()
				.loadKeyMaterial(keyStore, mch_id.toCharArray())       
				.build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
				sslcontext,
				new String[] { "TLSv1" },
				null,
				SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

		httpClient = HttpClients.custom()
				.setSSLSocketFactory(sslsf)
				.build(); 
		//根据默认超时限制初始化requestConfig
		requestConfig = RequestConfig.custom().setSocketTimeout(WechatUtil.getReadTimeoutMs()).setConnectTimeout(WechatUtil.getconnectTimeoutMs()).build();

	}

	/**
	 * 通过Https往API post xml数据
	 * @param url   API地址
	 * @param xmlObj   要提交的XML数据对象
	 * @param path    当前目录，用于加载证书
	 * @return
	 * @throws IOException
	 * @throws KeyStoreException
	 * @throws UnrecoverableKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public static String httpsRequest(String url,String mch_id,String path,String xmlObj) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
		//加载证书
		initCert(path,mch_id);
		String result = null;
		HttpPost httpPost = new HttpPost(url);
		//得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
		StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
		//很重要，用来检测 sdk 的使用情况，要不要加上商户信息？
		httpPost.addHeader("User-Agent", "wxpay sdk java v1.0 " + mch_id);  
		httpPost.addHeader("Content-Type", "text/xml");
		httpPost.setEntity(postEntity);
		//设置请求器的配置
		httpPost.setConfig(requestConfig);
		try {
			org.apache.http.HttpResponse response = httpClient.execute(httpPost);

			HttpEntity entity = response.getEntity();

			result = EntityUtils.toString(entity, "UTF-8");

			logger.info("上游响应回来信息result:"+result);
			System.out.println("result:"+result);
			logger.info("http get throw ConnectionPoolTimeoutException(wait time out)");
		} catch (ConnectionPoolTimeoutException e) {

		} catch (ConnectTimeoutException e) {
			logger.info("http get throw ConnectTimeoutException");

		} catch (SocketTimeoutException e) {
			logger.info("http get throw SocketTimeoutException");

		} catch (Exception e) {
			logger.info("http get throw Exception");

		} finally {
			httpPost.abort();
		}

		return result;
	}

}
